package example;

import java.util.LinkedList;
import java.util.List;

import utility.Word;

/**
 * Counts the number of words occurring in a passed in String.
 * 
 * @author Dallas Rosson (dallasjrosson@gmail.com)
 * @version 1.0
 */
public class WordCount {
	
	/**
	 * Constructs a new WordCount.
	 */
	public WordCount() {
		
		// Default constructor.
	}
	
	// Utility methods.
	
	/**
	 * Parses the passed in String into its sub-words, counts the occurrence of each word,
	 * and returns a list containing the words (as Words) in order of occurrence. 
	 * 
	 * @param the_string The String to be parsed for words.
	 * @param the_num_terms The number of terms to be returned.
	 * <i>(Note: If this value is greater than the number of words in the passed in String, 
	 * this method will only return a List containing the actual number of words.)</i>
	 * @return A List containing the Words in order of occurrence.
	 * @throws IllegalArgumentException if the_string is null.
	 * @throws IllegalArgumentException if the_string is empty.
	 */
	public List<Word> parseString(final String the_string, int the_num_terms) throws IllegalArgumentException{
		
		List<Word> parsedWords = new LinkedList<Word>();
		List<Word> sortedWords = new LinkedList<Word>();
		List<Word> retWords = new LinkedList<Word>();
		
		if (the_string == null || the_string.equals("")) { // Check for improper input.
			
			throw new IllegalArgumentException() ;
			
		} else { // Input is good.

			List<String> splitString =  stringSplitter(the_string);
			
			/*
			 * Pull the words from splitString, place them in a Word object, and then put
			 * them in parsedWords.  Replace dups as necessary, incrementing their counter.
			 */
			for (String s: splitString) {
				
				Word tempWord = new Word(s.toLowerCase()); // Ensure all words are lower case.
				
				/*
				 * If parsedWords contains the Word already, pull the word out, increment its
				 * counter, and put it back in parsedWords.
				 */
				if (parsedWords.contains(tempWord)) {
					
					int index = parsedWords.indexOf(tempWord);
					Word prevWord = parsedWords.remove(index);
					
					prevWord.addCount();
					
					parsedWords.add(prevWord);
					
				} else { // parsedWords doesn't contain the Word.  Put the word in the List.
					
					parsedWords.add(tempWord);
				}
			}
			
			/*
			 * Take all the Words from parsedWords and place them appropriately in retWords.
			 */
			for (Word w: parsedWords) {
								
				if (sortedWords.size() > 0) { // If retWords is not empty...
					
					for (int i = 0; i < sortedWords.size(); i++) {
						
						Word s = sortedWords.get(i);
						
						if (s.getCount() < w.getCount()) { // w has greater value. Place w ahead of s.
							
							int index = sortedWords.indexOf(s);
							sortedWords.add(index, w);
							break;
							
						} else if (s.getCount() == w.getCount()) {  // Equal count weight between s and w.
							
							/*
							 * Apply lexical sorting to the w and s.
							 */
							if (w.compareTo(s) < 0) { // w is ahead of s in the alphabet. Place w before s.
								
								int index = sortedWords.indexOf(s);
								sortedWords.add(index, w);
								break;
								
							} else if (i == sortedWords.size() - 1) { // End of List. Place w at the end of List.
								
								sortedWords.add(w);
								break;
							}
						} 
					}			
					
				} else { // If retWords is empty...
					
					sortedWords.add(w);
					
				}				
			}
			
			/*
			 * Ensure that the requested "n" terms is not greater than the actual number of words in the passed
			 * in String.
			 */
			if (the_num_terms > sortedWords.size()) {
				
				the_num_terms = sortedWords.size();
			}
			
			/*
			 * Create a List containing the top "n" terms.
			 */
			for (int i = 0; i < the_num_terms; i++) {
				
				retWords.add(sortedWords.get(i));
			}
			
		}
		
		return retWords;
	}
	
	/**
	 * Splits a string into separate words.  Removes spaces and periods.
	 * 
	 * @return A List of words (strings). 
	 */
	private List<String> stringSplitter(String the_string) {
		
		String delimiter = " ";
		
		String[] splitString;
		List<String> stringList = new LinkedList<String>();
		
		the_string = the_string.replace('.', ' '); // Remove all periods from the String.
		
		splitString = the_string.split(delimiter); // Split the string by spaces.
				
		for (String s: splitString) {
			
			if (!s.equals("")) { // Remove any empty Strings before returning the List.
				
				stringList.add(s);
			}
		}
		
		return stringList;
		
	}
}
